/*
 * This file is part of PDBEdit.
 *
 * PDBEdit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PDBEdit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PDBEdit.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Creation date : 15/09/2010
 * Copyright (c) Guerfyld 2010. All rights reserved.
 * http://......
 */
import pdbfile.*;
import filechooser.*;
import tablelist.*;

import java.util.Date;
import java.util.Calendar;
import javax.microedition.lcdui.*;
import javax.microedition.midlet.MIDlet;
import javax.microedition.io.*;
import javax.microedition.io.file.*;


/**
 * A MIDlet to edit PDB File.
 */
public class PDBEdit extends MIDlet implements CommandListener, TableSource, ItemCommandListener {
	private static Calendar cal = Calendar.getInstance();

	// Constants
	private static final String[] TRUEFALSELIST = {"Vrai", "Faux"};
	private static final String[] SORTORDER = {"Ascending", "Descending"};
	private static final String[] SEARCHOPTIONS = {"Down", "Begin", "Up", "End"};

	// View list display
	private List myViewList;
	private static final Command cmdExitViewList = new Command("Exit", Command.EXIT, 1);
	private static final Command cmdGoRecordList = new Command("Ok", Command.OK, 1);
	private static final Command cmdEditView     = new Command("Edit", Command.ITEM, 2);

	// Record list display
	private TableList myRecordList;
	private static final Command cmdBckToViewLst = new Command("Back", Command.BACK, 1);
	private static final Command cmdGoRecordDisp = new Command("Ok", Command.OK, 4);
	private static final Command cmdAddRecord    = new Command("Add", Command.ITEM, 1);
	private static final Command cmdCopyRecord   = new Command("Copy", Command.ITEM, 2);
	private static final Command cmdDelRecord    = new Command("Del", Command.ITEM, 3);
	private static final Command cmdSortRecord   = new Command("Sort", Command.ITEM, 3);
	private static final Command cmdSearchRecord = new Command("Search", Command.ITEM, 3);

	// Display record display
	private Form myRecord;
	private static final Command cmdBckToRecLst = new Command("Back", Command.BACK, 1);
	private static final Command cmdEditRecord  = new Command("Edit", Command.ITEM, 1);

	// Edit/Add record display
	private Form myEdit;
	private static final Command cmdBckToRecDsp = new Command("Back", Command.BACK, 1);
	private static final Command cmdOkAdd       = new Command("Valid", Command.OK, 1);
	private static final Command cmdOkEdit      = new Command("Valid", Command.OK, 1);

	private static final Command cmdDispNote    = new Command("Note", Command.ITEM, 3);
	private static final Command cmdEditNote    = new Command("Note", Command.ITEM, 3);
	private static final Command cmdLinkField   = new Command("Link", Command.ITEM, 3);

	private static final Command cmdExitNote    = new Command("Back", Command.BACK, 1);
	private static final Command cmdOkNote      = new Command("Ok", Command.OK, 1);
	private static final Command cmdOkLink      = new Command("Ok", Command.OK, 1);

	// Sort record display
	private Form orderForm = null;
	private ChoiceGroup sortCol;
	private ChoiceGroup sortOrder;
	private Command cmdOkSort      = new Command("Ok", Command.OK, 1);

	// Search record display
	private Form searchForm = null;
	private TextField searchText;
	private ChoiceGroup searchOptions;
	private Command cmdOkSearch    = new Command("Ok", Command.OK, 1);

	// Edit view column width display
	private Form viewForm;
	private Command cmdOkEditView   = new Command("Ok", Command.OK, 1);

	// Current selected file
	private FileChooser fc;
	// Current open file
	private PDBFile pdb;
	// Current selected view
	private PDBView view;
	// Current record values
	private PDBType[] vals;
	// Current edited item
	private Item itm;
	// Current linked file
	private PDBFile lfp;

	private class CustomTextField extends TextField {
		public int col;
		public CustomTextField(String label, String text, int maxSize, int constraints, int column) {
			super(label, text, maxSize, constraints);
			col = column;
		}
	}
	
	/**
	* Empty constructor.
	*/
		public PDBEdit() {
	}

	/**
	* Implementation of MIDlet :
	* Initialize the Displayables.
	*/
	public void startApp() {
		fc = new FileChooser(this, ".PDB");
		fc.setCommandListener(this);
		fc.showDir();
	}

	/**
	 * Implementation of MIDlet.
	 */
	public void pauseApp() {
	}

	/**
	 * Implementation of MIDlet.
	 */
	public void destroyApp(boolean unconditional) {
	}

	/**
	 * Implementation of CommandListener :
	 * Respond to a button push.
	 */
	public void commandAction(Command command, Displayable screen) {
	// File chooser commands :
		if(command == FileChooser.EXIT) {
			destroyApp(true);
			notifyDestroyed();
		}
		else if(command == FileChooser.OPEN) {
			new Thread(new Runnable() {
				public void run() {
					openFile();
				}
			}).start();
		}

	// List view commands :

		else if(command == cmdExitViewList) {
			new Thread(new Runnable() {
				public void run() {
					fc.showDir();
				}
			}).start();
		}
		else if(command == cmdEditView) {
			dispEditView();
		}
		else if(command == cmdGoRecordList) {
			dispRecordsList();
		}

		// Edit view commands

		else if(command == cmdOkEditView) {
			changeView();
		}

        // List records commands :

		else if(command == cmdBckToViewLst) {
			Display.getDisplay(this).setCurrent(myViewList);
				}
		else if(command == cmdGoRecordDisp) {
			dispRecord();
				}
		else if(command == cmdAddRecord) {
			addRecord(false);
				}
		else if(command == cmdCopyRecord) {
			addRecord(true);
				}
		else if(command == cmdDelRecord) {
			delRecord();
			dispRecordsList();
		}
		else if(command == cmdSortRecord) {
			dispSortOptions();
		}
		else if(command == cmdSearchRecord) {
			dispSearchOptions();
		}

        // View record commands :

		else if(command == cmdBckToRecLst) {
			Display.getDisplay(this).setCurrent(myRecordList);
		}
		else if(command == cmdEditRecord) {
			editRecord();
				}

        // Edit record commands :

		else if(command == cmdBckToRecDsp) {
			Display.getDisplay(this).setCurrent(myRecord);
		}
		else if(command == cmdOkEdit) {
			try {
				valid(false);
		                dispRecordsList();
			}
			catch (Exception e) {
				//System.out.println("ERREUR");
				Alert alert = new Alert("ERROR", e.toString(), null, AlertType.ERROR);
				alert.setTimeout(Alert.FOREVER);
				Display.getDisplay(this).setCurrent(alert);
			}
		}
		else if(command == cmdOkAdd) {
			try {
				valid(true);
				dispRecordsList();
			}
			catch (Exception e) {
				//System.out.println("ERREUR");
				Alert alert = new Alert("ERROR", e.toString(), null, AlertType.ERROR);
				alert.setTimeout(Alert.FOREVER);
				Display.getDisplay(this).setCurrent(alert);
			}
		}

        // Sort options commands :

		else if(command == cmdOkSort) {
			sortRecords();
		}

        // Search options commands :

		else if(command == cmdOkSearch) {
			searchRecords();
		}

	// Edit note commands :

		else if(command == cmdExitNote) {
			lfp = null;
			Display.getDisplay(this).setCurrentItem(itm);
		}
		else if(command == cmdOkNote) {
			// Get updated column
			int noteCol = ((CustomTextField)itm).col;
			// Update value
			String noteVal = ((TextBox)screen).getString();
			((PDBNote)vals[noteCol]).setValue(noteVal);
			// Display Edit form
			Display.getDisplay(this).setCurrentItem(itm);
		}
		else if(command == cmdOkLink) {
			// Get updated column
			int linkCol = ((CustomTextField)itm).col;
			// Update value
			int linkedCol = pdb.getColumn(linkCol).lnkColumn;
			//int linkedRow = ((List)screen).getSelectedIndex();
			int linkedRow = ((TableList)screen).getSelectedIndex();
			String linkedVal = "#ERR#";
			try {
				linkedVal = lfp.getRecord(linkedRow).getValue(linkedCol).toString();
			}
			catch (Exception e) {}
			((PDBLink)vals[linkCol]).setLnkRow(linkedRow);
			((PDBLink)vals[linkCol]).setValue(linkedVal);
			((CustomTextField)itm).setString(linkedVal);
			// Update linked values
			int nb = pdb.getNbCol();
			for (int c=0; c<nb; c++) {
				PDBColumn Col = pdb.getColumn(c);
				if ((Col.type == PDBColumn.LINKED) && (Col.linkedCol == linkCol)) {
					TextField tf = (TextField)myEdit.get(c);
					linkedVal = "#ERR#";
					try {
						linkedVal = lfp.getRecord(linkedRow).getValue(Col.lnkColumn).toString();
					}
					catch (Exception e) {}
					((PDBString)vals[c]).setValue(linkedVal);
					tf.setString(linkedVal);
				}
			}
			// Display Edit form
			lfp = null;
			Display.getDisplay(this).setCurrentItem(itm);
		}
	}

	/**
	 * Implementation of ItemCommandListener :
	 * Respond to a button push.
	 */
	public void commandAction(Command command, Item item) {
		if (command == cmdDispNote) {
			itm = item;
			dispNote(((CustomTextField)item).col, false);
		}
		else if (command == cmdEditNote) {
			itm = item;
			dispNote(((CustomTextField)item).col, true);
		}
		else if (command == cmdLinkField) {
			itm = item;
			new Thread(new Runnable() {
				public void run() {
					editLink(((CustomTextField)itm).col);
				}
			}).start();			
		}
	}

    /**
     * Read file
     */
	private void openFile() {
		try {
			FileConnection file = (FileConnection)Connector.open(fc.getFileUrl());
			if(file.canRead()) {
				pdb = new PDBFile();
				PDBInputStream datas = new PDBInputStream(file.openInputStream());
				pdb.readFile(datas);
				file.close();
				dispViewsList();
			}
		}
		catch(Exception e) {
			Alert alert = new Alert("ERROR", e.toString(), null, AlertType.ERROR);
			alert.setTimeout(Alert.FOREVER);
			Display.getDisplay(this).setCurrent(alert);
		}
	}

	/**
	 * Display Views list
	 */
	private void dispViewsList() {
		// Init List
		myViewList = new List("Views list", List.IMPLICIT);
		myViewList.addCommand(cmdExitViewList);
		myViewList.addCommand(cmdEditView);
		myViewList.setSelectCommand(cmdGoRecordList);
		myViewList.setCommandListener(this);
		orderForm = null;

		// Append List
		int nb = pdb.getNbView();
		for (int i=0; i<nb; i++)
			myViewList.append(pdb.getView(i).getName(), null);

		// Display List
		Display.getDisplay(this).setCurrent(myViewList);
	}

        /**
         * Display Records list
         */
        private void dispRecordsList() {
		// Set selected view
		int vwNum = myViewList.getSelectedIndex();
		view = pdb.getView(vwNum);

		// Init
                myRecordList = new TableList("Record list", this);
                myRecordList.addCommand(cmdBckToViewLst);
                myRecordList.addCommand(cmdAddRecord);
                myRecordList.addCommand(cmdDelRecord);
                myRecordList.addCommand(cmdSortRecord);
                myRecordList.addCommand(cmdSearchRecord);
                myRecordList.setSelectCommand(cmdGoRecordDisp);
                myRecordList.setCommandListener(this);
                Display.getDisplay(this).setCurrent(myRecordList);
        }

        /**
         * Display record values
         */
        private void dispRecord() {
		CustomTextField ctf;
                TextField tf;

                // Get selected record
                int recNum = myRecordList.getSelectedIndex();
                // Init
                myRecord = new Form("Record "+recNum);
                myRecord.addCommand(cmdBckToRecLst);
                myRecord.addCommand(cmdEditRecord);
                myRecord.setCommandListener(this);
		try {
	                // Append form
			vals = pdb.getRecord(recNum).getValues();
        	        int nb = pdb.getNbCol();
                	for(int c=0; c<nb; c++) {
				String name = pdb.getColumn(c).name;
				switch (pdb.getColumn(c).type) {
					case PDBColumn.BOOLEAN:
						tf = new TextField(name, vals[c].toString(), 5, TextField.UNEDITABLE);
						myRecord.append(tf);
						break;
					case PDBColumn.INTEGER:
						tf = new TextField(name, vals[c].toString(), 8, TextField.UNEDITABLE);
						myRecord.append(tf);
						break;
					case PDBColumn.DATE:
						tf = new TextField(name, vals[c].toString(), 12, TextField.UNEDITABLE);
						myRecord.append(tf);
						break;
					case PDBColumn.TIME:
						tf = new TextField(name, vals[c].toString(), 10, TextField.UNEDITABLE);
						myRecord.append(tf);
						break;
					case PDBColumn.LIST:
						tf = new TextField(name, vals[c].toString(), 25, TextField.UNEDITABLE);
						myRecord.append(tf);
						break;
					case PDBColumn.FLOAT:
						tf = new TextField(name, vals[c].toString(), 20, TextField.UNEDITABLE);
						myRecord.append(tf);
						break;
					case PDBColumn.NOTE:
						ctf = new CustomTextField(name, vals[c].toString(), 126, TextField.UNEDITABLE, c);
						ctf.setDefaultCommand(cmdDispNote);
						ctf.setItemCommandListener(this);
						myRecord.append(ctf);
						break;
					default:
						tf = new TextField(name, vals[c].toString(), 126, TextField.UNEDITABLE);
						myRecord.append(tf);
						break;
				}
			}

	                // Display form
			Display.getDisplay(this).setCurrent(myRecord);
		}
		catch (Exception e) {
			Alert alert = new Alert("ERROR", e.toString(), null, AlertType.ERROR);
			alert.setTimeout(Alert.FOREVER);
			Display.getDisplay(this).setCurrent(alert);
		}
	}

	/**
	 * Display view columns width
	 */
        private void dispEditView() {
		// Set selected view
		int vwNum = myViewList.getSelectedIndex();
		view = pdb.getView(vwNum);

		// Init Form
		viewForm = new Form("Edit view");
		viewForm.addCommand(cmdBckToViewLst);
		viewForm.addCommand(cmdOkEditView);
		viewForm.setCommandListener(this);

		// Append Form
		int nbCol = view.getNbCol();
		for(int p=0; p<nbCol; p++) {
			int c= view.getColNum(p);
			String name = pdb.getColumn(c).name;
			String width = Integer.toString(view.getColWidth(p));
			TextField tf = new TextField(name, width, 3, TextField.NUMERIC);
			viewForm.append(tf);
		}

		// Display Form
		Display.getDisplay(this).setCurrent(viewForm);
        }

	/**
	 * Change columns width of current view
	 */
	private void changeView() {
		// Update column width
		int nb = viewForm.size();
		for (int p=0; p < nb; p++) {
			TextField tf = (TextField)viewForm.get(p);
			view.setColWidth(p, Integer.parseInt(tf.getString()));
		}

		// Write change
		writeFile();

		Display.getDisplay(this).setCurrent(myViewList);
        }

        /**
         * Edit record values
         */
        private void editRecord() {
		// Init
		myEdit = new Form("Edit");
		myEdit.addCommand(cmdBckToRecDsp);
		myEdit.addCommand(cmdOkEdit);
		myEdit.setCommandListener(this);
		try {
			// Append form
			setEditFromVals();

			// Display form
			Display.getDisplay(this).setCurrent(myEdit);
		}
		catch (Exception e) {
			Alert alert = new Alert("ERROR", e.toString(), null, AlertType.ERROR);
			alert.setTimeout(Alert.FOREVER);
			Display.getDisplay(this).setCurrent(alert);
		}
	}

        /**
         * Add record values
         */
        private void addRecord(boolean copy) {
                // Init
                myEdit = new Form("Add");
                myEdit.addCommand(cmdBckToRecLst);
                myEdit.addCommand(cmdOkAdd);
                myEdit.setCommandListener(this);
		try {
	                // Append form
			if (copy) {
		                // Get values of selected record
	        	        int recNum = myRecordList.getSelectedIndex();
				vals = pdb.getRecord(recNum).getValues();
			}
			else {
				vals = pdb.getColumnsDefault();
			}
			setEditFromVals();

			// Display form
	                Display.getDisplay(this).setCurrent(myEdit);
		}
		catch (Exception e) {
			Alert alert = new Alert("ERROR", e.toString(), null, AlertType.ERROR);
			alert.setTimeout(Alert.FOREVER);
			Display.getDisplay(this).setCurrent(alert);
		}
        }

	// Append record values to Form
	private void setEditFromVals() throws Exception {
                TextField tf;
		CustomTextField ctf;
                DateField df;
                ChoiceGroup cg;
		StringItem  si;
		long lngVal;

		int nb = pdb.getNbCol();
		for(int c=0; c<nb; c++) {
			//System.out.print("Column "+c+" : "+vals[c]);
			String name = pdb.getColumn(c).name;
			switch (pdb.getColumn(c).type) {
			case PDBColumn.STRING:
				tf = new TextField(name, vals[c].toString(), 126, TextField.ANY);
				myEdit.append(tf);
				break;
			case PDBColumn.LINKED:
				tf = new TextField(name, vals[c].toString(), 126, TextField.UNEDITABLE);
				myEdit.append(tf);
				break;
			case PDBColumn.LINK:
				ctf = new CustomTextField(name, vals[c].toString(), 126, TextField.UNEDITABLE, c);
				ctf.setDefaultCommand(cmdLinkField);
				ctf.setItemCommandListener(this);
				myEdit.append(ctf);
				break;
			case PDBColumn.NOTE:
				ctf = new CustomTextField(name, vals[c].toString(), 126, TextField.ANY, c);
				ctf.setDefaultCommand(cmdEditNote);
				ctf.setItemCommandListener(this);
				myEdit.append(ctf);
				break;
			case PDBColumn.BOOLEAN:
				cg = new ChoiceGroup(name, ChoiceGroup.POPUP,
				TRUEFALSELIST, null);
				cg.setSelectedIndex(((PDBBoolean)vals[c]).getValue(), true);
				myEdit.append(cg);
				break;
			case PDBColumn.INTEGER:
				tf = new TextField(name, vals[c].toString(), 8, TextField.NUMERIC);
				myEdit.append(tf);
				break;
			case PDBColumn.DATE:
				df = new DateField(name, DateField.DATE);
				lngVal = ((PDBDate)vals[c]).getValue();
				if (lngVal == 0)
					df.setDate(new Date());
				else
					df.setDate(value2date(((PDBDate)vals[c]).getValue()));
				myEdit.append(df);
				break;
			case PDBColumn.TIME:
				df = new DateField(name, DateField.TIME);
				lngVal = ((PDBTime)vals[c]).getValue();
				if (lngVal == 0)
					df.setDate(new Date());
				else
					df.setDate(value2time(((PDBTime)vals[c]).getValue()));
				myEdit.append(df);
				break;
			case PDBColumn.LIST:
				cg = new ChoiceGroup(name, ChoiceGroup.POPUP,
				((PDBList)vals[c]).getList(), null);
				try {
					cg.setSelectedIndex(((PDBList)vals[c]).getValue(), true);
				} 
				catch (Exception e) {}
				myEdit.append(cg);
				break;
			case PDBColumn.FLOAT:
				tf = new TextField(name, vals[c].toString(), 10, TextField.DECIMAL);
				myEdit.append(tf);
				break;
			}
			//System.out.println(" -> Ok");
		}
	}

	// Get values from Form
	private void getValsFromEdit() {
		String strVal;
		int intVal;
		long lngVal;
		Date dteVal;
		double dblVal;

		int nb = pdb.getNbCol();
		for(int c=0; c<nb; c++) {
			switch (pdb.getColumn(c).type) {
			case PDBColumn.STRING:
			case PDBColumn.LINK:
			case PDBColumn.LINKED:
				strVal = ((TextField)myEdit.get(c)).getString();
				((PDBString)vals[c]).setValue(strVal);
				break;
			case PDBColumn.NOTE:
				strVal = ((TextField)myEdit.get(c)).getString();
				((PDBNote)vals[c]).setTitle(strVal);
				break;
			case PDBColumn.BOOLEAN:
				intVal = ((ChoiceGroup)myEdit.get(c)).getSelectedIndex();
				((PDBBoolean)vals[c]).setValue(intVal);
				break;
			case PDBColumn.INTEGER:
				lngVal = Long.parseLong(((TextField)myEdit.get(c)).getString());
				((PDBInteger)vals[c]).setValue(lngVal);
				break;
			case PDBColumn.DATE:
				dteVal = ((DateField)myEdit.get(c)).getDate();
				((PDBDate)vals[c]).setValue(date2value(dteVal));
				break;
			case PDBColumn.TIME:
				dteVal = ((DateField)myEdit.get(c)).getDate();
				((PDBTime)vals[c]).setValue(time2value(dteVal));
				break;
			case PDBColumn.LIST:
				intVal = ((ChoiceGroup)myEdit.get(c)).getSelectedIndex();
				((PDBList)vals[c]).setValue(intVal);
				break;
			case PDBColumn.FLOAT:
				dblVal = Double.parseDouble(((TextField)myEdit.get(c)).getString());
				((PDBFloat)vals[c]).setValue(dblVal);
				break;
			}
		}
	}

	/**
	 * Delete a record
	 */
	private void delRecord() {
		// Get selected record
		int recNum = myRecordList.getSelectedIndex();
		pdb.delRecord(recNum);

                // Write change
		writeFile();
	}

	/**
	 * Validate updates
	 */
	private void valid(boolean add) throws Exception {
		PDBRecord rec;

		// Update vals array
		getValsFromEdit();

		if (add) {
			rec = pdb.getNewRecord();
		}
		else {
			int recNum = myRecordList.getSelectedIndex();
			rec = pdb.getRecord(recNum);
		}
		int nb = pdb.getNbCol();
		for(int c=0; c<nb; c++) {
			// Update modified values only
			PDBType val = rec.getValue(c);
			if ((val == null) || (vals[c].compareTo(val) != 0)) {
				rec.setValue(vals[c], c);
			}
		}

		// Write change
		writeFile();
	}

	/**
	 * Display sort options
	 */
	private void dispSortOptions() {
		if (orderForm == null) {
			// Init Form
			orderForm = new Form("Sort");
			orderForm.addCommand(cmdOkSort);
			orderForm.addCommand(cmdBckToRecLst);
			orderForm.setCommandListener(this);

			// Append Form
			sortOrder = new ChoiceGroup("Order", ChoiceGroup.POPUP, SORTORDER, null);
			orderForm.append(sortOrder);
			int nbCol = view.getNbCol();
			String[] colList = new String[nbCol];
			for(int p=0; p<nbCol; p++) {
				int c= view.getColNum(p);
				colList[p] = pdb.getColumn(c).name;
			}
			sortCol = new ChoiceGroup("Column", ChoiceGroup.EXCLUSIVE, colList, null);
			orderForm.append(sortCol);
		}

		// Display Form
		Display.getDisplay(this).setCurrent(orderForm);
	}

	/**
	 * Sort file
	 */
	private void sortRecords() {
		try {
			// Sort file
			pdb.sort(sortCol.getSelectedIndex(), sortOrder.getSelectedIndex());

			// Write change
			writeFile();

			// Display result
			dispRecordsList();
		}
		catch (Exception e) {
			e.printStackTrace();
			Alert alert = new Alert("ERROR", e.toString(), null, AlertType.ERROR);
			alert.setTimeout(Alert.FOREVER);
			Display.getDisplay(this).setCurrent(alert);
		}
	}

    /**
     * Display search options
     */
    private void dispSearchOptions() {
           if (searchForm == null) {
              searchForm = new Form("Search");
              searchText = new TextField("Search", "", 20, TextField.ANY);
              searchForm.append(searchText);
              searchOptions = new ChoiceGroup("Options", ChoiceGroup.EXCLUSIVE, SEARCHOPTIONS, null);
              searchForm.append(searchOptions);

              searchForm.addCommand(cmdOkSearch);
              searchForm.addCommand(cmdBckToRecLst);
              searchForm.setCommandListener(this);
           }

           Display.getDisplay(this).setCurrent(searchForm);
    }

	/**
	 * Search record
	 */
	private void searchRecords() {
		int numView = myViewList.getSelectedIndex();
		String txt = searchText.getString();
		int opt = searchOptions.getSelectedIndex();
		int start = 0;
		int direction = 1;
		switch (opt) {
		case 0: // Down
			start = myRecordList.getSelectedIndex()+1;
			break;
		case 1: // Begin
			start = 0;
			break;
		case 2: // Up
			start = myRecordList.getSelectedIndex()-1;
			direction = -1;
			break;
		case 3: // End
			start = pdb.getNbRec()-1;
			direction = -1;
			break;
		}
		try {
			int rowFound = pdb.search(numView, txt, start, direction);
			if (rowFound != -1) {
				myRecordList.setSelectedIndex(rowFound);

				// Display result
				Display.getDisplay(this).setCurrent(myRecordList);
			}
			else {
				Alert alert = new Alert("INFO", "No record found !", null, AlertType.INFO);
				alert.setTimeout(2000);
				Display.getDisplay(this).setCurrent(alert);
			}
		}
		catch (Exception e) {
			e.printStackTrace();
			Alert alert = new Alert("ERROR", e.toString(), null, AlertType.ERROR);
			alert.setTimeout(Alert.FOREVER);
			Display.getDisplay(this).setCurrent(alert);
		}
	}

	/**
	 * Display note
	 */
	private void dispNote(int col, boolean edit) {
		// Create form
		TextBox tb = new TextBox("Note", ((PDBNote)vals[col]).getValue(), 1024, edit ? TextField.ANY : TextField.UNEDITABLE);
		tb.addCommand(cmdExitNote);
		if (edit) tb.addCommand(cmdOkNote);
		tb.setCommandListener(this);

		// Display form
		Display.getDisplay(this).setCurrent(tb);
	}

	/**
	 * Display link
	 */
	private void editLink(int col) {
		final int width = myViewList.getWidth();
		// Open linked file
		String lfn = pdb.getColumn(col).lnkFilename;
		try {
			FileConnection file = (FileConnection)Connector.open("file://"+fc.getCurDir()+lfn+".pdb");
			//System.out.println("open : file://"+fc.getCurDir()+lfn+".pdb");
			if(file.canRead()) {
				PDBInputStream datas = new PDBInputStream(file.openInputStream());
				lfp = new PDBFile();
				lfp.readFile(datas);
				file.close();

				// Create list
				//List lnkList = new List("Link", Choice.IMPLICIT);
				final TableList lnkList = new TableList("Link", new TableSource() {
					public int getRowCount() {
						return lfp.getNbRec();
					}
					public int getColCount() {
						return 1;
					}
					public String[] getHeader() {
						return null;
					}
					public int[] getColWidth() { 
						int[] ret = new int[1];
						ret[0] = width-2;
						return ret;
					}
					public String getValue(int row, int col) {
						String value = null;
						PDBRecord rec = lfp.getRecord(row);
						try {
							int nbc = lfp.getNbCol();
							for (int c=0; c<nbc; c++) {
								if (c==0) value = rec.getValue(c).toString();
								else value += " - " + rec.getValue(c).toString();
							}
						}
						catch (Exception e) {}
						return value;
					}
				});
				lnkList.addCommand(cmdExitNote);
				//lnkList.addCommand(cmdOkLink);
				lnkList.setSelectCommand(cmdOkLink);
				lnkList.setCommandListener(this);

				// Append list
				int nb = lfp.getNbRec();
				for (int r=0; r<nb; r++) {
					//lnkList.append(lfp.getRecord(r).toString(), null);
				}
				//lnkList.setSelectedIndex((int)((PDBLink)vals[col]).getLnkRow(), true);
				lnkList.setSelectedIndex((int)((PDBLink)vals[col]).getLnkRow());

				// Display list
				Display.getDisplay(this).setCurrent(lnkList);
			}
		}
		catch(Exception e) {
			Alert alert = new Alert("ERROR", e.toString(), null, AlertType.ERROR);
			alert.setTimeout(Alert.FOREVER);
			Display.getDisplay(this).setCurrent(alert);
		}
	}

	/**
	 * Write file (in another thread)
	 */
	private void writeFile() {
                   new Thread(new Runnable() {
                           public void run() {
                        try {
                                FileConnection file = (FileConnection)Connector.open(fc.getFileUrl());
				PDBOutputStream datas = new PDBOutputStream(file.openOutputStream());
                                pdb.writeFile(datas);
                                file.close();
                        }
                        catch (Exception e) {
                            e.printStackTrace();
                        }
            }
        }).start();
        }

	/* TableSource implementation */
	public int getRowCount() {
		return pdb.getNbRec();
	}

	public int getColCount() {
		return view.getNbCol();
	}

	public String[] getHeader() {
		String[] colName = null;
		int nb = view.getNbCol();
		colName = new String[nb];
		for (int i = 0; i < nb; i++) {
			int c = view.getColNum(i);
			colName[i] = pdb.getColumn(c).name;
		}
		return colName;
	}

	public int[] getColWidth() {
		int[] colWidth = null;
		int nb = view.getNbCol();
		colWidth = new int[nb];
		for (int i = 0; i < nb; i++) {
			colWidth[i] = view.getColWidth(i);
		}
		return colWidth;
	}

	public String getValue(int row, int col) {
		String value = null;
		int c = view.getColNum(col);
		PDBRecord rec = pdb.getRecord(row);
		try {
			value = rec.getValue(c).toString();
		}
		catch (Exception e) {}
		return value;
	}

	// Convert Date to PilotDB date field raw value
	private static int date2value(Date date) {
		int val = PDBDate.UNDEFINED;
		if (date != null) {
			cal.setTime(date);
			val = cal.get(Calendar.YEAR) << 16;
			val += (cal.get(Calendar.MONTH)+1) << 8; //0=Jan...11=Dec
			val += cal.get(Calendar.DAY_OF_MONTH);
		}
		return val;
	}

	// Convert PilotDB date field raw value to Date
	private static Date value2date(int val) {
		cal.set(Calendar.YEAR, val >> 16);
		cal.set(Calendar.MONTH, ((val >> 8) & 0xFF)-1); //0=Jan...11=Dec
		cal.set(Calendar.DAY_OF_MONTH, val & 0xFF);
		cal.set(Calendar.HOUR, 0);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		try {
			return cal.getTime();
		}
		catch (Exception e) {
			return null;
		}
	}

	// Convert PilotDB time field raw value to Date
	private static Date value2time(int val) {
		cal.set(Calendar.YEAR, 0);
		cal.set(Calendar.MONTH, 0); //0=Jan...11=Dec
		cal.set(Calendar.DAY_OF_MONTH, 1);
		cal.set(Calendar.HOUR, (val >> 8) & 0xFF);
		cal.set(Calendar.MINUTE, val & 0xFF);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		try {
			return cal.getTime();
		}
		catch (Exception e) {
			return null;
		}
	}

	// Convert Date to PilotDB date field raw value
	private static int time2value(Date date) {
		int val = PDBTime.UNDEFINED;
		if (date != null) {
			cal.setTime(date);
			val = cal.get(Calendar.HOUR);
			if ((cal.get(Calendar.AM_PM) == Calendar.PM) && (val < 12)) val += 12;
			val = val << 8;
			val += cal.get(Calendar.MINUTE);
		}
		return val;
	}
}
